package org.dromara.hutool.core.lang;


import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Match类实现了一个类似于模式匹配的结构，允许根据给定的条件（matchValue）返回相应的结果（returnValue）。
 * 它提供了多种静态工厂方法来创建Match实例，包括空实例、基于值和基于Supplier的实例。
 * 该类支持map和flatMap操作，可以对matchValue和returnValue应用转换函数。
 * 还提供了条件匹配的方法，可以根据给定的谓词返回新的Match实例或保持当前状态。
 * 此外，Match类实现了处理缺失值的机制，提供了orElse、orElseGet和orElseThrow方法来处理返回值的缺失情况。
 * <pre>
 * <code>
 *      MatchV2.of(matchString, "def")
 *          .caseNull("is null")
 *          .caseValue("null", "null")
 *          .caseValue(String::isEmpty, "is empty")
 *          .caseValue(StringUtils::isBlank, ()->"is blank");
 * </code>
 * </pre>
 *
 * @param <T> 用于匹配的值类型.
 * @param <R> 用于返回的值类型.
 * @author tanglt
 * @version jdk 8
 */
public class Match<T,R> {


    /**
     * 判断的元素
     */
    private final T matchValue;

    /**
     * 返回的元素
     */
    private final R returnValue;

    /**
     * 是否命中条件
     */
    private final boolean hit;

    /**
     * 默认返回的元素
     */
    private final R defValue;

    /**
     * {@code Match}的构造函数
     *
     */
    private Match(final T matchValue, final R defValue) {
        this.matchValue = matchValue;
        this.returnValue = null;
        this.defValue = defValue;
        this.hit = false;
    }
    /**
     * {@code Match}的构造函数
     *
     */
    private Match(final T matchValue, final R returnValue, final R defValue, boolean hit) {
        this.matchValue = matchValue;
        this.returnValue = returnValue;
        this.defValue = defValue;
        this.hit = hit;
    }

	/**
	 * 传入匹配的值和默认返回值，包装一个Match
	 *
	 * @param matchValue 匹配的值
	 * @param defValue 默认返回值
	 * @return {@code Match}
	 */
    public static <T,R> Match<T,R> of(final T matchValue,final R defValue) {
        return new Match<>(matchValue,defValue);
    }


	/**
	 * 传入匹配的值，无默认返回值，包装一个Match
	 *
	 * @param matchValue 匹配的值
	 * @return {@code Match}
	 */
    public static <T,R> Match<T,R> of(final T matchValue) {
        return new Match<>(matchValue,null);
    }

	/**
	 * 可以拼接两个Match，如果第一个Match条件命中，则后一个Match无效。反之，后一个条件生效。
	 *
	 * @param mapper 需要合并的第二个Match的BiFunction，第一个值是前一个Match的匹配值，第二个值是前一个Match的返回值
	 * @param <M>    新的匹配值类型
	 * @return {@code Match}
	 */
    public <M> Match<M,R> flatMap(final BiFunction<? super T, ? super R, ? extends Match<M,R>> mapper) {
        Objects.requireNonNull(mapper);
        Match<M, R> mrMatch = mapper.apply(this.matchValue ,isPresent()?this.returnValue:this.defValue);
        if (isPresent()) {
            return new Match<>(mrMatch.matchValue,this.returnValue,mrMatch.defValue,true);
        } else {
            return mrMatch;
        }
    }

	/**
	 * 映射匹配值。
	 *
	 * @param matchMapper    新的匹配值映射
	 * @param <U>    新的匹配值类型
	 * @return {@code Match}
	 */
    public <U> Match<U,R> mapMatch(final Function<? super T, ? extends U> matchMapper) {
        Objects.requireNonNull(matchMapper);
        return new Match<>(matchMapper.apply(this.matchValue),this.returnValue,this.defValue,this.hit);
    }

	/**
	 * 映射返回值。如果返回值还没有，则映射默认返回值
	 *
	 * @param returnMapper    新的返回值映射
	 * @param <F>    新的返回值类型
	 * @return {@code Match}
	 */
    public <F> Match<T,F> map(final Function<? super R, ? extends F> returnMapper) {
        Objects.requireNonNull(returnMapper);
        if(isPresent()){
            F returnValue = returnMapper.apply(this.returnValue);
            return new Match<>(this.matchValue,returnValue,returnValue,this.hit);
        }else {
            F defValue = returnMapper.apply(this.defValue);
            return new Match<>(this.matchValue,null,defValue,this.hit);
        }
    }

	/**
	 * 判断是否和匹配值相等，并指定返回的值
	 *
	 * @param caseValue 判断的值
	 * @param returnValue  指定返回的值
	 * @return {@code Match}
	 */
    public Match<T,R> caseValue(final T caseValue, final R returnValue) {
        Objects.requireNonNull(caseValue);
        if(isPresent()||!Objects.equals(this.matchValue,caseValue)){
            return this;
        }
        return new Match<>(this.matchValue,returnValue,this.defValue,true);
    }

	/**
	 * 使用谓词判断是否和匹配值相等，并指定返回的值。可以在类似BigDecimal相等判断的时候使用
	 *
	 * @param predicate 谓词条件
	 * @param returnValue  指定返回的值
	 * @return {@code Match}
	 */
    public Match<T,R> caseValue(final Predicate<? super T> predicate, final R returnValue) {
        Objects.requireNonNull(predicate);
        if(isPresent()||!predicate.test(this.matchValue)){
            return this;
        }
        return new Match<>(this.matchValue,returnValue,this.defValue,true);
    }

	/**
	 * 使用谓词判断是否和匹配值相等，并指定返回的值的。使用Supplier可以在复杂计算返回值的情况下延迟计算，提高性能和规避空异常
	 *
	 * @param predicate 谓词条件
	 * @param supplier  指定返回值Supplier
	 * @return {@code Match}
	 */
	public Match<T,R> caseValue(final Predicate<? super T> predicate, final Supplier<R> supplier) {
		Objects.requireNonNull(predicate);
		Objects.requireNonNull(supplier);
		if(isPresent()||!predicate.test(this.matchValue)){
			return this;
		}
		return new Match<>(this.matchValue,supplier.get(),this.defValue,true);
	}


	/**
	 * 检查null的判断条件.
	 *
	 * @param returnValue  指定返回的值
	 * @return {@code Match}
	 */
	public Match<T,R> caseNull(final R returnValue) {
        if(isPresent()||!Objects.isNull(this.matchValue)){
            return this;
        }
        return new Match<>(null,returnValue,this.defValue,true);
    }

	/**
	 * 检查null的判断条件.使用Supplier返回，可以在复杂计算返回值的情况下延迟计算
	 *
	 * @param supplier  指定返回值的Supplier
	 * @return {@code Match}
	 */
    public Match<T,R> caseNull(final Supplier<R> supplier) {
        Objects.requireNonNull(supplier);
        if(isPresent()||!Objects.isNull(this.matchValue)){
            return this;
        }
        return new Match<>(null,supplier.get(),this.defValue,true);
    }

	/**
	 * 返回条件命中的值或者默认值
	 * @return <R>  条件命中的值或者默认值的类型
	 */
    public R get() {
        return isPresent() ? this.returnValue : this.defValue;
    }

	/**
	 * 返回条件命中的值。如果没有命中，返回传入值
	 * @param other  没有命中，返回的值
	 * @return <R>  条件命中的值或者默认值的类型
	 */
    public R orElse(final R other) {
        return isPresent() ? this.returnValue : other;
    }


	/**
	 * 返回条件命中的值。如果没有命中，返回Supplier的值，可以在复杂计算返回值的情况下延迟计算
	 * @param supplier  没有命中，返回值的Supplier
	 * @return <R>  条件命中的值或者默认值的类型
	 */
    public R orElseGet(final Supplier<? extends R> supplier) {
        return isPresent() ? this.returnValue : supplier.get();
    }


	/**
	 * 返回条件命中的值。如果没有命中，返回Supplier 可以抛出异常
	 * @param exceptionSupplier  没有命中，抛出异常
	 * @return <R>  条件命中的值或者抛出异常
	 */
    public <X extends Throwable> R orElseThrow(final Supplier<? extends X> exceptionSupplier) throws X{
        if (isPresent()) {
            return this.returnValue;
        }
        throw exceptionSupplier.get();
    }

	/**
	 * 返回条件是否命中结果
	 *
	 * @return boolean  条件是否命中结果
	 */
    public boolean isPresent() {
        return this.hit;
    }


    @Override
    public boolean equals(Object o) {
        return o == this || ( o instanceof Match && Objects.equals(o.toString(),this.toString()));
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(this.toString());
    }

    @Override
    public String toString() {
        return "matchValue("
                +this.matchValue+
                "),returnValue("
                +this.returnValue+
                "),hit("
                +this.hit+
                "),defValue("
                +this.defValue+")";
    }
}
